use std::sync::Arc;
use crate::{
    abstractions::{data::Data, portable_task::TaskPortableAbs, task::TaskAbstraction},
    protocol::type_schema::TypeSchema, workflows::workflow::Workflow,
    protocol::type_derive::FuseDirection::*
};

pub struct BuiltinMisc;

impl BuiltinMisc {
    pub fn copy_file_path() -> Arc<TaskAbstraction> {
        let input_type = TypeSchema::Str;
        let output_type = TypeSchema::from_str("(Str, Str, Str)");
        let taskfunc = move | input: Data | {
            let path = input.as_string();
            Ok(Data::DataTuple(vec![Data::DataStr(path.clone()), Data::DataStr(path.clone()), Data::DataStr(path)]))
        };
        Arc::new(TaskAbstraction::Builtin("copy_file_path".into(), input_type, output_type, Arc::new(taskfunc)))
    }
    pub fn list_add_1() -> Arc<TaskAbstraction> {
        let input_type = TypeSchema::List(Box::new(TypeSchema::Int));
        let output_type = TypeSchema::List(Box::new(TypeSchema::Int));
        let taskfunc = move | input: Data | {
            let list = input.try_as_vec()?;
            let mut new_list = Vec::new();
            for item in list {
                new_list.push(Data::DataInt(item.try_as_int()? + 1));
            }
            Ok(Data::DataList(new_list))
        };
        Arc::new(TaskAbstraction::Builtin("list_add_1".into(), input_type, output_type, Arc::new(taskfunc)))
    }
    pub fn int_mul_2() -> Arc<TaskAbstraction> {
        let input_type = TypeSchema::Int;
        let output_type = TypeSchema::Int;
        let taskfunc = move | input: Data | {
            let int = input.try_as_int()?;
            Ok(Data::DataInt(int * 2))
        };
        Arc::new(TaskAbstraction::Builtin("int_mul_2".into(), input_type, output_type, Arc::new(taskfunc)))
    }
    pub fn list_add_1_portable() -> TaskPortableAbs {
        let input_type = TypeSchema::List(Box::new(TypeSchema::Int));
        let output_type = TypeSchema::List(Box::new(TypeSchema::Int));
        let taskfunc = move | input: Data, _: u16 | {
            let list = input.try_as_vec()?;
            let mut new_list = Vec::new();
            for item in list {
                new_list.push(Data::DataInt(item.try_as_int()? + 1));
            }
            Ok(Data::DataList(new_list))
        };
        TaskPortableAbs::Simple("list_add_1".into(), input_type, output_type, Arc::new(taskfunc), 0, vec![])
    }
    pub fn int_mul_2_portable() -> TaskPortableAbs {
        let input_type = TypeSchema::Int;
        let output_type = TypeSchema::Int;
        let taskfunc = move | input: Data, _: u16 | {
            let int = input.try_as_int()?;
            Ok(Data::DataInt(int * 2))
        };
        TaskPortableAbs::Simple("int_mul_2".into(), input_type, output_type, Arc::new(taskfunc), 0, vec![])
    }
    pub fn calculate_1() -> Workflow {
        let input_type = TypeSchema::from_str("List<Int>");
        let output_type = TypeSchema::from_str("List<Int>");
        Workflow::Static("calculate_1".into(), input_type, output_type, vec![BuiltinMisc::list_add_1(), BuiltinMisc::int_mul_2()], vec![(Positive, false), (Positive, false)])
    }
}
